package reflect_extend

import (
	"reflect"
	"runtime"
	"strings"
	"unsafe"
)

// 结构体内部字段首字母小写，并且是非取地址访问 ,可以使用下面的方法访问
func GetPtrUnExportedField(s interface{}, field string) reflect.Value {
	v := reflect.ValueOf(s)
	//判断是否是指针
	if v.Kind() != reflect.Ptr {
		panic("s 入参必须是指针")
	}
	name := v.Elem().FieldByName(field)
	if !name.IsValid() {
		// 如果字段不存在或无法访问，返回零值
		return reflect.Zero(reflect.TypeOf(s).Elem())
	}
	// 创建一个新的Value，类型为v的类型，内存地址与v相同
	return reflect.NewAt(name.Type(), unsafe.Pointer(name.UnsafeAddr())).Elem()
}

// 结构体内部字段首字母小写，并且是取地址访问 ,可以使用下面的方法访问
func GetPtrFiled(s interface{}, filed string) reflect.Value {
	//判断是否是指针
	if IsPtr(s) {
		panic("s 入参必须是指针")
	}
	v := reflect.ValueOf(s).Elem().FieldByName(filed)
	// 必须要调用 Elem()
	return reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()
}

// 获取变量的类型
func GetType(v interface{}) reflect.Type {
	return reflect.TypeOf(v)
}

// 获取指针变量的类型
func GetPtrType(v interface{}) reflect.Type {
	//判断是否是指针
	if !IsPtr(v) {
		panic("v 入参必须是指针")
	}
	elem := reflect.TypeOf(v).Elem()
	//判断如果还是指针,则继续取值
	if elem.Kind() == reflect.Ptr {
		elem1 := reflect.ValueOf(v).Elem() //获取指针的值
		//在继续判断类型
		return GetPtrType(elem1.Interface())
	}
	return elem
}

// 获取指针的值 ,直接转换为目标的对象了, 而不是 reflect.Value
// 所以切记不能使用这个来修改值,可以使用GetPtrValueElem
func GetPtrValue(v interface{}) interface{} {
	return GetPtrValueElem(v).Interface()
}
func GetPtrValueElem(v interface{}) reflect.Value {
	of := reflect.ValueOf(v)
	//判断是否是指针
	if of.Kind() != reflect.Ptr {
		panic("v 入参必须是指针")
	}
	elem := of.Elem()
	//判断如果还是指针,则继续取值
	if elem.Kind() == reflect.Ptr {
		return GetPtrValueElem(elem.Interface())
	}
	return elem

}

// 获取函数返回的类型
// 一般来说直接传递函数的定义那么一定是指针
func GetFuncRetType(f interface{}) []reflect.Type {
	//判断是否是指针
	if IsPtr(f) {
		f = GetPtrValue(f)
	}
	t := reflect.TypeOf(f)

	if t.Kind() != reflect.Func {
		panic("f must be a function")
	}
	numOut := t.NumOut()
	retTypes := make([]reflect.Type, numOut)
	for i := 0; i < numOut; i++ {
		retTypes[i] = t.Out(i)
	}
	return retTypes
}

// 获取函数入参类型
func GetFuncInType(f interface{}) []reflect.Type {
	//判断是否是指针
	if IsPtr(f) {
		f = GetPtrValue(f)
	}
	t := reflect.TypeOf(f)
	if t.Kind() != reflect.Func {
		panic("f must be a function")
	}
	numIn := t.NumIn()
	inTypes := make([]reflect.Type, numIn)
	for i := 0; i < numIn; i++ {
		inTypes[i] = t.In(i)
	}
	return inTypes

}

// 获取结构体字段类型,返回 map[字段名]字段类型,只处理可导出字段
func GetStructFieldTypes(s interface{}) map[string]reflect.Type {
	var t reflect.Type
	if c, ok := s.(reflect.Type); ok {
		t = c
	} else {
		//判断是否是指针
		if IsPtr(s) {
			s = GetPtrValue(s)
		}
		t = reflect.TypeOf(s)
	}
	if t.Kind() != reflect.Struct {
		panic("s must be a struct")
	}
	numField := t.NumField()
	fieldTypes := make(map[string]reflect.Type)
	for i := 0; i < numField; i++ {
		field := t.Field(i)
		//判断是否可用,如果不是空字符串,则不可用
		if field.PkgPath != "" {
			continue
		}
		fieldTypes[field.Name] = field.Type
	}
	return fieldTypes
}

// 因为reflect.Value 用于修改值,所以要求s必须是指针,如果使用的是interface{},那么其内部必须是指针
func GetStructFieldValues(s interface{}) map[string]reflect.Value {
	v := reflect.ValueOf(s)
	elem := v.Elem()
	if v.Kind() != reflect.Ptr || elem.Kind() != reflect.Struct {
		panic("s 必须是结构体指针")
	}
	numField := elem.NumField()
	fieldValues := make(map[string]reflect.Value)
	t := elem.Type()
	for i := 0; i < numField; i++ {
		field := t.Field(i)
		//判断是否可用,如果不是空字符串,则不可用
		if field.PkgPath != "" {
			continue
		}
		fieldValues[field.Name] = elem.Field(i)
	}
	return fieldValues

}

// 获取结构体字段标签,返回 map[字段名]字段标签,只处理可导出字段
func GetStructFieldTags(s interface{}, tagName string) map[string]string {
	//判断是否是指针
	if IsPtr(s) {
		s = GetPtrValue(s)
	}

	t := reflect.TypeOf(s)
	if t.Kind() != reflect.Struct {
		panic("s must be a struct")
	}
	numField := t.NumField()
	fieldTags := make(map[string]string)
	for i := 0; i < numField; i++ {
		field := t.Field(i)
		//判断是否可用,如果不是空字符串,则不可用
		if field.PkgPath != "" {
			continue
		}
		fieldTags[field.Name] = field.Tag.Get(tagName)
	}
	return fieldTags
}

func GetStructField(s interface{}) map[string]reflect.StructField {
	//判断是否是指针
	if IsPtr(s) {
		s = GetPtrValue(s)
	}
	t := reflect.TypeOf(s)
	if t.Kind() != reflect.Struct {
		panic("s must be a struct")
	}
	numField := t.NumField()
	fields := make(map[string]reflect.StructField)
	for i := 0; i < numField; i++ {
		fields[t.Field(i).Name] = t.Field(i)
	}
	return fields
}

type StructInfo struct {
	Field reflect.StructField
	Value reflect.Value
	Type  reflect.Type
}

func GetStructFieldInfo(s interface{}) map[string]StructInfo {
	elem := reflect.ValueOf(s).Elem()
	//判断是否是指针
	if IsPtr(s) {
		s = GetPtrValue(s)
	}
	t := reflect.TypeOf(s)
	if t.Kind() != reflect.Struct {
		panic("s must be a struct")
	}
	numField := t.NumField()
	fields := make(map[string]StructInfo)
	for i := 0; i < numField; i++ {
		field := t.Field(i)
		fields[field.Name] = StructInfo{
			Field: field,
			Value: elem.Field(i),
			Type:  field.Type,
		}
	}
	return fields

}

// 获取类型名称支持: 函数,结构体,指针, 变量
// 不支持空接口
func GetDefName(v interface{}) string {
	//判断是否是指针
	if IsPtr(v) {
		v = GetPtrValue(v)
	}
	//如果是函数
	if GetType(v).Kind() == reflect.Func {
		return runtime.FuncForPC(reflect.ValueOf(v).Pointer()).Name()
	}
	return reflect.TypeOf(v).Name()
}

// 获取全路径名称
func GetDefFullName(v interface{}) string {
	//判断是指针并且是接口
	if IsPtr(v) && GetType(v).Elem().Kind() == reflect.Interface {
		return GetPtrType(v).PkgPath() + "." + GetPtrType(v).Name()
	}
	//判断是否是指针
	if IsPtr(v) {
		v = GetPtrValue(v)
	}

	//如果是函数
	if GetType(v).Kind() == reflect.Func {
		return runtime.FuncForPC(reflect.ValueOf(v).Pointer()).Name()
	}
	return reflect.TypeOf(v).PkgPath() + "." + reflect.TypeOf(v).Name()
}

// 获取函数名称
func GetFuncName(v interface{}) string {
	//判断是否是指针
	if IsPtr(v) {
		v = GetPtrValue(v)
	}
	//如果是函数
	if GetType(v).Kind() == reflect.Func {
		name := runtime.FuncForPC(reflect.ValueOf(v).Pointer()).Name()
		//去掉路径
		name = name[strings.LastIndex(name, "/")+1:]
		//去掉包名
		name = name[strings.LastIndex(name, ".")+1:]
		return name
	}
	panic("v must be a function")
}

// 获取结构体实现的接口 ,这种方法只能获取
//
//	type MyStruct struct {
//		ExportedField   int
//		unexportedField string
//		MyInterface  //嵌套接口的方式
//	}
//
// 通过函数实现的接口无法获取, 因为 go 没有提供向上查找的方法
func GetStructInterface(s interface{}) []reflect.Type {
	//判断如果是 reflect.Type
	var t reflect.Type
	if c, ok := s.(reflect.Type); ok {
		t = c
	} else {
		//判断是否是指针
		if IsPtr(s) {
			s = GetPtrValue(s)
		}

		t1 := reflect.TypeOf(s)
		if t1.Kind() != reflect.Struct {
			panic("s must be a struct")
		}
		t = t1
	}

	numField := t.NumField()
	var interfaceTypes []reflect.Type
	for i := 0; i < numField; i++ {
		field := t.Field(i)
		//判断是否可用,如果不是空字符串,则不可用
		if field.PkgPath != "" {
			continue
		}
		if field.Type.Kind() == reflect.Interface {
			interfaceTypes = append(interfaceTypes, field.Type)
		}
	}
	return interfaceTypes
}

// 判断是否是基本类型
func IsBasicType(v interface{}) bool {
	kind := GetType(v).Kind()
	return kind >= reflect.Bool && kind <= reflect.Complex128
}

// 获取结构体全部的字段 field
func GetStructFields(s interface{}) []reflect.StructField {
	//判断是否是指针
	if IsPtr(s) {
		s = GetPtrValue(s)
	}
	t := reflect.TypeOf(s)
	if t.Kind() != reflect.Struct {
		panic("s must be a struct")
	}
	numField := t.NumField()
	fields := make([]reflect.StructField, numField)
	for i := 0; i < numField; i++ {
		fields[i] = t.Field(i)
	}
	return fields
}
